package br.com.seimos.commons.flex.adapter;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.hibernate.collection.PersistentBag;

import br.com.seimos.commons.reflection.Reflection;
import flex.messaging.messages.Message;
import flex.messaging.services.remoting.adapters.JavaAdapter;

/**
 * Adapter java/flex verificando lazy persistent bag e tornando-as nula quando
 * não estiverem inicializadas.
 * 
 * @author Moesio Medeiros
 * 
 */
public class LazyJavaAdapter extends JavaAdapter {
	ArrayList<Object> lazyStack = new ArrayList<Object>();

	public Object invoke(Message message) {
		Object result = super.invoke(message);

		lazyStack = new ArrayList<Object>();
		nullateLazyBag(result);

		return result;
	}

	/**
	 * Anulando persistentBag n�o iniciados
	 * 
	 * @date 09 abr 2009
	 * @param result
	 */
	private void nullateLazyBag(Object result) {
		if (result instanceof Collection<?>) {
			for (Object o : (Collection<?>) result) {
				nullateLazyBag(o);
			}
		} else if (result != null && !Reflection.isEntity(result.getClass())) {
			return;
		} else {
			if (result != null) {
				if (!lazyStack.contains(result)) {
					lazyStack.add(result);
					Field[] fields = Reflection.getNoTransientFields(result.getClass());
					for (Field field : fields) {
						Object invoke = Reflection.invoke(result, field.getName());
						if (invoke == null) {
							continue;
						} else {
							/*
							 * if (result instanceof Menu && (field.getName() ==
							 * "perfils" || field.getName() == "menus")) {
							 * System.out.println(); }
							 */
							if (invoke instanceof PersistentBag && !((PersistentBag) invoke).wasInitialized()) { 
								// se for um bolsa vazia, anule-a
								nullate(result, field.getName());
							} else if (Reflection.isEntity(invoke.getClass()) || (invoke instanceof PersistentBag && ((PersistentBag) invoke).wasInitialized())) {
								if (lazyStack.contains(invoke)) {
									nullate(result, field.getName());
								} else {
									nullateLazyBag(invoke);
								}
							}
						}
					}
					lazyStack.remove(result);
				} else {
					nullateAnyBag(result);
				}
			}
		}
	}

	private void nullateAnyBag(Object result) {
		Class<?> clazz = result.getClass();
		try {
			result = clazz.newInstance();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
		// Field[] fields = getNoTransientFields(result.getClass());
		// for (Field field : fields)
		// {
		// Object invoke = invoke(result, field.getName());
		//
		// if (invoke != null && invoke instanceof PersistentBag)
		// {
		// nullate(result, field.getName());
		// }
		// }
	}

	private void nullate(Object entity, String property) {
		Class<?> clazz = entity.getClass();

		// Necess�rio testar o tipo de argumento que o setter recebe
		// Algumas persistentBag foram mapeadas como List e outras como
		// Collection
		try {
			Method method = clazz.getMethod(Reflection.getSetter(property), new Class[] { List.class });
			method.invoke(entity, new Object[] { null });
		} catch (Exception e) {
			try {

				Method method = clazz.getMethod(Reflection.getSetter(property), new Class[] { Collection.class });
				method.invoke(entity, new Object[] { null });
			} catch (Exception e1) {
				try {
					Method method = clazz.getMethod(Reflection.getSetter(property), new Class[] { Reflection.invoke(entity, property).getClass() });
					method.invoke(entity, new Object[] { null });
				} catch (Exception e2) {
					// em caso de n�o existir o m�todo. Praticamente imposs�vel,
					// j� que estou trazendo um m�todo preexistente
					e.printStackTrace();
				}
			}
		}
	}
}
